﻿using System;
using System.Collections.Generic;


namespace myLittleC
{
    class Parser
    {
        protected SyntaxNode obj = null;

        protected bool MayDeclaration {get; set;}

        protected Tokenizer mTokenizer;

        public Parser(String file)
        {
            MayDeclaration = false;
            mTokenizer = new Tokenizer(file);
        }

        protected virtual bool IsGlobal() { return false; }

        public virtual void Parse()
        {           
             if (mTokenizer.TokenType != TokenType.EOF)
                obj = ParseStatement();
             if (mTokenizer.TokenType != TokenType.EOF)
                throw new ComException(ERRORS.BadExpression, mTokenizer.Position);
        }
       
        public override String ToString() { return ((SyntaxExpression) obj).ToString("", true); }

        private static bool IsDeclareVareable(String name) { return true; }

        private SyntaxNode ParseStatement() { return ParseExpr(); }

        protected SyntaxExpression ParseExpr() 
        {
            var exp = ParseBinaryExpr(0);
            exp = exp.Calculate();
            CheckExpr(exp);
            return exp; 
        }

        protected virtual SymType CheckExpr(SyntaxExpression exp) {  return new SymTypeInt(); }

        private SyntaxExpression ParseBinaryExpr(int prioritet)
        {
            var left = (prioritet + 1 < 10) ? ParseBinaryExpr(prioritet + 1) : ParseUnaryExpr();

            if (prioritet == 0 && CheckPrioritet(prioritet, mTokenizer.SubType) && !IsLValue(ref left))
                 throw new ComException(ERRORS.BadLValue, mTokenizer.Position);
            
            while (CheckPrioritet(prioritet, mTokenizer.SubType))
            {
                var stt = mTokenizer.SubType;
                mTokenizer.GetNextToken();
                var right = ParseBinaryExpr(prioritet + (prioritet == 0 ? 0 :1));
                
                if (right == null)
                    throw new ComException(((mTokenizer.SubType == SubTokenType.EOF) ? ERRORS.EOF : ERRORS.BadExpression), mTokenizer.Position);
                left = new SyntaxBinaryOp(stt, left, right);
            }
            return left;
        }

        SyntaxExpression ParseCallFunk(String identName)
        {
            var args = new LinkedList<SyntaxExpression>();
            mTokenizer++;

            while (!mTokenizer.isFinishParanthesis())
            {
                args.AddLast(ParseExpr());

                if (mTokenizer.isFinishParanthesis()) break;
                if (!mTokenizer.IsComma())
                    throw new ComException(ERRORS.ExpComma, mTokenizer.Position);

                mTokenizer++;
            }

            mTokenizer++;
            return  new SyntaxCallFunc(identName, args);
        }

        SyntaxExpression ParseParanthesis()
        {
            mTokenizer++;
            var parsedExpresion = ParseExpr();

            if (!mTokenizer.isFinishParanthesis())
                throw new ComException(ERRORS.ExpRightParan, mTokenizer.Position);

            mTokenizer++;
            return parsedExpresion;
        }

        SyntaxExpression ParseStruct(SyntaxIdentefier left)
        {
            mTokenizer++;
                    
            var right = ParseUnaryExpr();

            if (!(right is SyntaxIdentefier || right is SyntaxDotOp))
                throw new ComException(ERRORS.NotIdent, mTokenizer.Position);

            return new SyntaxDotOp(left, right);
        }

        SyntaxExpression ParseLArrow(SyntaxIdentefier left)
        {
            mTokenizer++;

            var right = ParseUnaryExpr();

            if (!(right is SyntaxIdentefier || right is SyntaxDotOp))
                throw new ComException(ERRORS.NotIdent, mTokenizer.Position);

            return new SyntaxlArrowOp(left, right);
        }

        SyntaxExpression ParseIndexOfArray(SyntaxExpression left)
        {
            var right = new List<SyntaxExpression>();

            while (mTokenizer.isStartSquareBracket())
            {
                mTokenizer++;
                right.Add(ParseUnaryExpr());

                if (!mTokenizer.isFinishSquareBracket())
                    throw new ComException(ERRORS.ExpFinishSquareBracket, mTokenizer.Position);

                mTokenizer++;
            }

            return new SyntaxIndexArray(left, ref right);
        }

        SyntaxExpression ParseIdenteficator()
        {
            SyntaxExpression parsedExpresion;
            if (!IsDeclareVareable(mTokenizer.ActiveToken.Value.ToString()))
                throw new ComException(ERRORS.DublDeclaration, mTokenizer.ActiveToken.Position);

            var identName = mTokenizer.ActiveToken.Value as String;
            ++mTokenizer;

            if (!mTokenizer.isStartParanthesis())
            {
                parsedExpresion = new SyntaxIdentefier(identName, IsGlobal());
                if (mTokenizer.IsDot())
                    parsedExpresion = ParseStruct(parsedExpresion as SyntaxIdentefier);
                else if (mTokenizer.IsLArrow())
                    parsedExpresion = ParseLArrow(parsedExpresion as SyntaxIdentefier);
                else if (mTokenizer.isStartSquareBracket())
                    parsedExpresion = ParseIndexOfArray(parsedExpresion);
                else if (mTokenizer.IsIncrementOrDecrement())
                {
                    parsedExpresion = new SyntaxUnaryOp(mTokenizer.SubType, parsedExpresion, true);
                    mTokenizer++;
                }
                else if (mTokenizer.IsIdenteficator() || mTokenizer.IsConst() || mTokenizer.isStartParanthesis())
                    throw new ComException(ERRORS.BadExpression, mTokenizer.Position);
            }
            else 
                parsedExpresion = ParseCallFunk(identName);

            return parsedExpresion;
        }

        SyntaxExpression ParseUnaryExpr()
        {
            SyntaxExpression parsedExpresion;

            if (mTokenizer.isStartParanthesis())
                parsedExpresion = ParseParanthesis();
            else if (mTokenizer.IsIdenteficator()) 
                parsedExpresion = ParseIdenteficator();
            //TODO: проверяем если * или & значит указатель
            else if (mTokenizer.SubType == SubTokenType.Multiply
                || mTokenizer.SubType == SubTokenType.And)
            {
                var operation = mTokenizer.SubType;
                ++mTokenizer;
                parsedExpresion = new SyntaxUnaryOp(operation, ParseUnaryExpr());
            }
            else if (mTokenizer.IsConst())
            {
                parsedExpresion = null;
                if (mTokenizer.IsConstInt())
                    parsedExpresion = new SyntaxConstInt(mTokenizer.ActiveToken);
                else if (mTokenizer.IsConstReal())
                    parsedExpresion = new SyntaxConstReal(mTokenizer.ActiveToken);
                else if (mTokenizer.IsConstString())
                    parsedExpresion = new SyntaxConstString(mTokenizer.ActiveToken);

                mTokenizer++;

                if (mTokenizer.IsNoType())
                    throw new ComException(ERRORS.BadUnaryExp, mTokenizer.Position);
                if (mTokenizer.IsIdenteficator() || mTokenizer.IsConst() || mTokenizer.isStartParanthesis())
                    throw new ComException(ERRORS.BadExpression, mTokenizer.Position);
            }
            else if (mTokenizer.IsUnaryOp())
            {
                SubTokenType stt = mTokenizer.SubType;
                mTokenizer++;
                parsedExpresion = ParseUnaryExpr();
                if (parsedExpresion == null)
                    throw new ComException((mTokenizer.TokenType == TokenType.EOF ? ERRORS.EOF : ERRORS.BadUnaryExp), mTokenizer.Position);
                if (parsedExpresion is SyntaxConst && (stt == SubTokenType.Increment || stt == SubTokenType.Decrement))
                    throw new ComException(ERRORS.BadLValue, mTokenizer.Position);

                parsedExpresion = new SyntaxUnaryOp(stt, parsedExpresion);
            }

            else
                throw new ComException(ERRORS.BadExpression, mTokenizer.Position);

            return parsedExpresion;
        }

        private static bool CheckPrioritet(int prioritet, SubTokenType subType)
        {
            if (SubTokenType.Increment <= subType && subType <= SubTokenType.Not || SubTokenType.Dot == subType)
                return prioritet == 10;
            if (SubTokenType.Multiply <= subType && subType <= SubTokenType.Div)
                return prioritet == 9;
            if (SubTokenType.Add == subType || subType == SubTokenType.Sub)
                return prioritet == 8;
            if (SubTokenType.SHL == subType || subType == SubTokenType.SHR)
                return prioritet == 7;
            if (SubTokenType.Low <= subType && subType <= SubTokenType.HiOrEq)
                return prioritet == 6;
            if (SubTokenType.Equal == subType || subType == SubTokenType.NotEqual)
                return prioritet == 5;
            if (SubTokenType.And == subType)
                return prioritet == 4;
            if (SubTokenType.Or == subType)
                return prioritet == 3;
            if (SubTokenType.LogicalAnd == subType)
                return prioritet == 2;
            if (SubTokenType.LogicalOr == subType)
                return prioritet == 1;
            if (SubTokenType.Assignment <= subType && subType <= SubTokenType.DivAssignment)
                return prioritet == 0;

            return false;
        }

        private static bool IsLValue(ref SyntaxExpression left)
        {
            return (left is SyntaxIdentefier || (left is SyntaxUnaryOp && (left as SyntaxUnaryOp).Operation == SubTokenType.Multiply)
              || left is SyntaxIndexArray || left is SyntaxDotOp);
        }
    }

    class ParserWithStatement : Parser
    {
        public void Optimise(){   Global.Optimise();}

        public SyntaxBlock Global { get; private set;}
        SyntaxBlock _useBlock;
        private bool _inFunc = false;

        public override void Parse()
        {
            if (mTokenizer.TokenType != TokenType.EOF)
                ParseBlock();
            obj = Global;
            if (mTokenizer.TokenType != TokenType.EOF)
                throw new ComException(ERRORS.BadExpression, mTokenizer.Position);
        }

        private SyntaxBlock CreateGlobal()
        {
            Global = new SyntaxBlock(); 
            Global.Add(new SymTypeInt());
            Global.Add(new SymTypeFloat());
            Global.Add(new SymTypeCharsConst());

            SymbolTable ts = new SymbolTable();
            ts.Add(new SymVar("FormString", Global.FindType("charsconsts")));
            ts.Add(new SymVar("data", Global.FindType("int")));

            Global.Add(new SymFunc("printf", Global.FindType("int"), ts));
            return Global;
        }
       
        public ParserWithStatement(String file) : base(file)  {}

        public String WriteTable() { return Global.WriteTable("", "Global"); }

        public override string ToString() { return Global.ToString(); }

        private bool UniqIdenteficator
        {   get{   return mTokenizer.IsIdenteficator() && _useBlock.Find(mTokenizer.Name) == null;}}

        private bool isType(String TypeName) 
		{   return mTokenizer.ResTypes() || _useBlock.FindTypeWithParent(TypeName) != null; }
		
        private bool MayDeclareFunc() {	return _useBlock == Global;}
		
        private bool MayDefinitionFunc(String FuncName)
        {
			SymFunc  func = _useBlock.Table.FindFunc(FuncName);
            return MayDeclareFunc() && func != null && func.IsPrototipe ;
		}  

        private SymType FindType(SyntaxBlock iBlock)
        {   return FindType(mTokenizer.Name);}

        private SymType FindType(String iType)
        {
            var t = (SymType)_useBlock.FindTypeWithParent( iType);
            return (t is SymTypedef) ? (t as SymTypedef).Type : t;
        }

        private SyntaxNode parseStatement(SyntaxBlock iBlock)
        {
            iBlock.EndDefentions();

            switch (mTokenizer.SubType)
            {   
                case SubTokenType.RwDo:     return ParseDoWhile();
                case SubTokenType.RwIf:     return ParseIf();
                case SubTokenType.RwFor:    return ParseFor();
                case SubTokenType.RwWhile:  return ParseWhile();
                case SubTokenType.RwReturn: return ParseReturn();

                case SubTokenType.SpttLbrace:
                    mTokenizer++;
                return ParseBlock();
              
                case SubTokenType.RwElse:   
                    throw new ComException(ERRORS.ElseBeforeIf, mTokenizer.Position);

                default:
                    SyntaxNode res  = ParseExpr();
                    if (!mTokenizer.IsSemicolon())
                        throw new ComException(ERRORS.ExpSemicolon, mTokenizer.Position);
                    mTokenizer++;
                return res;
            }
        }


        private SyntaxBlock ParseBlock()
        {
            _useBlock = Global == null ? CreateGlobal() : new SyntaxBlock(_useBlock);
            return ParseBlock(_useBlock);
        }

        private SyntaxBlock ParseBlock(SyntaxBlock iBlock)
        {
            _useBlock = iBlock;
            while (!mTokenizer.isFinishBrace() && !mTokenizer.IsEnd())
            {
                if (isType(mTokenizer.Name))
                {
                    SymType type = FindType(_useBlock);
                    mTokenizer++;
                    ParseDefinition( type);
                }
                else if (mTokenizer.isStruct())
                {
                    mTokenizer++;
                    SymType type = ParseDefinitionStruct();

                    if (!mTokenizer.IsIdenteficator() && !mTokenizer.IsSemicolon())
                         throw new ComException(ERRORS.UnIdefSymbol, mTokenizer.Position);
                    
                    if (mTokenizer.IsIdenteficator())
                        ParseDefinition(type);
                    else 
                        mTokenizer++;
                }
				else if (mTokenizer.isTypedef())
				{
					mTokenizer++;
                    _useBlock.Add(ParseDefinitionTypedef(_useBlock));
					mTokenizer++;
				}
                else
                { 
                    if (!_inFunc)
                        throw new ComException(ERRORS.OutOfFunc, mTokenizer.Position);
                   _useBlock.Add(parseStatement(iBlock));                    
                }
            }

            if (mTokenizer.IsEnd() && _useBlock != Global)
                throw new ComException(ERRORS.ExpFinBrace);

            mTokenizer++;
            
            var retBloc = _useBlock;
            _useBlock = _useBlock.Parrent;
            return retBloc;
        }


        #region Различные обьявления

        private SymType CreateLinkForType(SymType iType)
        {   return FindType("*" + iType.Name) ?? (SymType) _useBlock.Add(new SymTypeLink(iType));}

        private void ParseDefinition(SymType iType)
        {
            var useType = iType;

            if (mTokenizer.SubType == SubTokenType.Multiply)
            {
                mTokenizer++;
                useType = CreateLinkForType(iType);
            }

            if (!(mTokenizer).IsIdenteficator())
                throw new ComException(ERRORS.ExpIdent, mTokenizer.Position);
           
            var name = mTokenizer.Name;
          
            if (UniqIdenteficator)
            {
                if (!_useBlock.MayDefs)
                    throw new ComException(ERRORS.CantDefsVar, mTokenizer.Position);

                if (mTokenizer++.isStartParanthesis()) 
                {
                    if (!MayDeclareFunc())
                        throw new ComException(ERRORS.CantDefsFunc, mTokenizer.Position);
                    var newFunk = ParseDeclareFunc(name, useType);
                    _useBlock.Add(newFunk);
                    
            
                    if (mTokenizer++.SubType == SubTokenType.SpttLbrace)
                    {
                        newFunk.Body = ParseDefinitionFunc(newFunk.Args, newFunk);
                        //TODO: проверить тип
                        //TODO: разобраться с ретурном, мб убрать на него проверку
                        //if (((SyntaxReturn)newFunk.Body.SyntaxNodes[newFunk.Body.SyntaxNodes.Count - 1]).RetValue.ExType != newFunk.RetType)
                          //  throw new  ComException(ERRORS.TypesNotCoincide, mTokenizer.Position);
                    }
                    else
                    {
                        if (!mTokenizer.IsSemicolon())
                            throw new ComException(ERRORS.ExpSemicolon);
                        mTokenizer++;
                    }
            
                }
                else 
                {
                    _useBlock.Add(DefineVar(name, useType));
                    while (mTokenizer.IsComma())
                    {
                        useType = iType;
                        mTokenizer++;

                        if (mTokenizer.SubType == SubTokenType.Multiply)
                        {
                            mTokenizer++;
                            useType = CreateLinkForType(iType);
                        }

                        if (!UniqIdenteficator)
							throw new ComException(ERRORS.NotUniqDef, mTokenizer.Position);
                        
                        name = mTokenizer.Name;
                        mTokenizer++;
                        _useBlock.Add(DefineVar(name, useType));
                    }
                   
                    if (!mTokenizer.IsSemicolon())
                        throw new ComException(ERRORS.ExpSemicolon, mTokenizer.Position);
                    mTokenizer++;
                }
            }
            else if ((mTokenizer++).isStartParanthesis())
			{
				if (!MayDefinitionFunc(name))
                    throw new ComException(
                          _useBlock.Table.FindFunc(name) == null ?
			             	ERRORS.NotUniqDef : ERRORS.CantDefsFunc, mTokenizer.Position);
	            
                SymFunc func = _useBlock.Table.FindFunc(name);
	            for (; !mTokenizer.isStartBrace(); mTokenizer++) ;
                func.Body = ParseDefinitionFunc(func.Args, func);
			}
            else
                 throw new ComException(ERRORS.NotUniqDef, mTokenizer.Position);
        }

        private ISymbol DefineVar(String iName, SymType iType)
        {
            ISymbol newVar;
            if (mTokenizer.isStartSquareBracket())
                newVar = ParseDefinitionArrays(iName, iType);
            else 
            {
                newVar = new SymVar(iName, iType);
                if (mTokenizer.IsAssignment())
                {
                    mTokenizer++;
                    (newVar as SymVar).Value = ParseExpr();
                }
            }
            return newVar;
        }


        private SymFunc ParseDeclareFunc(String Name, SymType Type)
        {
            SymbolTable Args = new SymbolTable();
            
            if (!(++mTokenizer).isFinishParanthesis())
            {
                do
                {
                    if (mTokenizer.IsComma()) mTokenizer++;
                    if (!isType(mTokenizer.Name))
                        throw new ComException(ERRORS.ExpType, mTokenizer.Position);
                    
					SymType type = FindType(Global);

                    if ((mTokenizer++).SubType == SubTokenType.Multiply)
                    {
                        mTokenizer++;
                        type = CreateLinkForType(type);
                    }

                    Args.Add(new SymVar(mTokenizer.Name, type, false, true));
                    mTokenizer++;
                }
                while (!mTokenizer.isFinishParanthesis() && (mTokenizer).IsComma());
            }
           
            return new SymFunc(Name, Type, Args);
            
        }

        private SyntaxBlock ParseDefinitionFunc(SymbolTable Args, SymFunc iFunc)
        {
            _inFunc = true;
            SyntaxBlock Body = new SyntaxBlock(Global, ref iFunc);
            Body.Table.Copy(Args);
            mTokenizer++;
            Body = ParseBlock(Body);
           // if (Body.SyntaxNodes.Count == 0 || !(Body.SyntaxNodes[Body.SyntaxNodes.Count - 1] is SyntaxReturn))
             //   throw new ComException(ERRORS.ExpRetValue, mTokenizer.Position);
            
            _useBlock = Global;
            _inFunc = false;
            return Body;
        }

        private SymVarArray ParseDefinitionArrays(String iName, SymType iType)
        {
            var arraySize= new List<UInt64>();

            do
            {
                mTokenizer++;
                if (!(mTokenizer.ActiveToken.Value is Int32))
                    throw new ComException(ERRORS.NotArraySize, mTokenizer.Position);

                arraySize.Add( Convert.ToUInt64(mTokenizer.ActiveToken.Value));
            
                if (!mTokenizer++.isFinishSquareBracket())
                    throw new ComException(ERRORS.ExpRBreak, mTokenizer.Position);
                
            } while ((++mTokenizer).isStartSquareBracket());
            
            var type = new SymTypeArray(iType, ref arraySize);
          
            return new SymVarArray(iName, type);
        }

        private SymStruct ParseDefinitionStruct()
        {
            SymStruct newStuct ;
            if (mTokenizer.IsIdenteficator())
			//	throw new ComExeption(ERRORS.UnExpIdent, mTokenizer.Position);
			{
	            if (UniqIdenteficator) 
				{	
					newStuct = new SymStruct(mTokenizer.Name); 
					_useBlock.Add(newStuct);
				}
				else
				{
                    ISymbol sym = _useBlock.Table.FindType(mTokenizer.Name);
	                if ( !( sym is SymStruct) || !(sym as SymStruct).IsPrototipe)                
						throw new ComException(ERRORS.NotUniqDef, mTokenizer.Position);
					newStuct = (sym as SymStruct);  
				}	
				mTokenizer++;
			}
			else 
                _useBlock.Add(newStuct = new SymStruct());
			
            if (mTokenizer.isStartBrace())
            {
                mTokenizer++;
                SyntaxBlock StructBody = ParseBlock();

                if (StructBody.HaveNodes) throw new ComException(ERRORS.CanDefsExpr, mTokenizer.Position);
                if (!StructBody.Table.HaveVars) throw new ComException(ERRORS.EmpryStruct, mTokenizer.Position);

                newStuct.Items = StructBody.Table;
            }
            return newStuct;
        }

        private SymTypedef ParseDefinitionTypedef(SyntaxBlock iBlock)
        {
			SymType Type  ;
			if (isType(mTokenizer.Name))
			{
				Type  = FindType(iBlock);
				mTokenizer++;
			}
			else 
			{
			   	mTokenizer++;
				Type  = ParseDefinitionStruct();	
			}
            
            
            if (!(mTokenizer).IsIdenteficator()) throw new ComException(ERRORS.ExpIdent, mTokenizer.Position);
			String name = mTokenizer.Name;
			
			if(!(++mTokenizer).IsSemicolon()) throw new ComException(ERRORS.ExpSemicolon);
			
			return new SymTypedef(name, Type);
        }

        #endregion

        #region Парсинг операторов
       
        /// <summary>
        /// Функция парсит тело оператора
        /// </summary>
        /// <returns>
        /// Собственно само тело в возвращаеться в блоке
        /// </returns>
        
        private SyntaxBlock ParseOperatorsBoby()
        {
            SyntaxBlock Body;
            if (mTokenizer.isStartBrace())
            {
                mTokenizer++;
                Body = ParseBlock(); 
            }
            else
            {//TODO: подправить
                Body = new SyntaxBlock(_useBlock);
                Body.Add( parseStatement(_useBlock));
            }

            return Body;
        }

        protected override bool IsGlobal()   {return _useBlock == Global;}

        private SyntaxExpression ParseCondition()
        {
            if (!mTokenizer.isStartParanthesis())
                throw new ComException(ERRORS.ExpStartParan, mTokenizer.Position);

            mTokenizer++;
            SyntaxExpression Condition = ParseExpr();

            if (!mTokenizer.isFinishParanthesis())
                throw new ComException(ERRORS.ExpFinishParan, mTokenizer.Position);
            return Condition;
        }

      
        private SyntaxWhile ParseWhile()
        {
            mTokenizer++;
            SyntaxExpression condition = ParseCondition();
            mTokenizer++;
            return new SyntaxWhile(condition, ParseOperatorsBoby());
        }

        private SyntaxDoWhile ParseDoWhile()
        {
            mTokenizer++;
            SyntaxBlock body = ParseOperatorsBoby();

            if (!mTokenizer.isWhile())
                throw new ComException(ERRORS.ExpWhile, mTokenizer.Position);
            mTokenizer++;
            SyntaxExpression con = ParseCondition();
            mTokenizer++;
            if (!mTokenizer.IsSemicolon())
                throw new ComException(ERRORS.ExpSemicolon, mTokenizer.Position);
            mTokenizer++;
            return new SyntaxDoWhile(con, body);
        }

        private SyntaxFor ParseFor()
        {
            SyntaxExpression[] head = new SyntaxExpression[3];
            mTokenizer++;
            if (!mTokenizer.isStartParanthesis())
                throw new ComException(ERRORS.ExpStartParan, mTokenizer.Position);

            mTokenizer++;
           
            //TODO: вот тут вылетает ошибка 
            
           if (!mTokenizer.IsSemicolon())
                head[0] = ParseExpr();

            for (int i = 1; i <= 2; i++)
            {
                if (!mTokenizer.IsSemicolon())
                    throw new ComException(ERRORS.ExpSemicolon, mTokenizer.Position);
               
                mTokenizer++;
                if (!mTokenizer.IsSemicolon() && !mTokenizer.isFinishParanthesis())
                    head[i] = ParseExpr();
            }
            
            if (!mTokenizer.isFinishParanthesis())
                throw new ComException(ERRORS.ExpFinishParan, mTokenizer.Position);
            mTokenizer++;
            return new SyntaxFor(head, ParseOperatorsBoby());
        }

        private SyntaxIf ParseIf(SyntaxIf iParent)
        {
            SyntaxExpression Condition = null;

            if (mTokenizer.isIf())
            {
                if (iParent != null && iParent.IsElse)
                    throw new ComException(ERRORS.ElseBeforeElseIf, mTokenizer.Position);
                mTokenizer++;
                Condition = ParseCondition();
                mTokenizer++;
            }

            SyntaxIf RetIff = new SyntaxIf(Condition, ParseOperatorsBoby(), iParent);
          
            if (mTokenizer.isElse())
            {
                mTokenizer++;
                RetIff.Next = ParseIf(RetIff);
            }
            return RetIff;
        }

        private SyntaxIf ParseIf() {   return ParseIf(null);}

        #endregion

        #region оставшиеся операторы

        private SyntaxReturn ParseReturn()
        {
            if (!_inFunc)
                throw new ComException(ERRORS.ReturnWithOutFunc, mTokenizer.Position);
            mTokenizer++;
            SyntaxExpression exp = ParseExpr();
           
            if (!mTokenizer.IsSemicolon())
                throw new ComException(ERRORS.ExpSemicolon, mTokenizer.Position);
            mTokenizer++;
            return new SyntaxReturn(exp);
        }
        #endregion

        #region Проверка типов
        protected override SymType CheckExpr(SyntaxExpression exp)
        {
            if (exp.ExType != null)
                return exp.ExType;

            if (exp is SyntaxlArrowOp)
                CheckLArrow(exp as SyntaxlArrowOp);
            else if (exp is SyntaxDotOp)
                CheckStruct(exp as SyntaxDotOp);
            else if (exp is SyntaxIndexArray)
                CheckIndexArray(exp as SyntaxIndexArray);
            else if (exp is SyntaxBinaryOp)
                CheckBinaryOp(exp as SyntaxBinaryOp); 
            else if (exp is SyntaxUnaryOp)
                CheckUnaryOp(exp as SyntaxUnaryOp);
            else if (exp is SyntaxIdentefier)
                exp.ExType = CheckIdentefier(exp as SyntaxIdentefier);
            else if (exp is SyntaxConst)
                exp.ExType = CheckConst(exp as SyntaxConst);
            else if (exp is SyntaxCallFunc)
                exp.ExType = CheckCallFunc(exp as SyntaxCallFunc);
            else
                throw new ComException(ERRORS.UnKnownOp, mTokenizer.Position);
            return exp.ExType;
        }

        private SymType CheckLArrow(SyntaxlArrowOp  exp)
        {

            if (exp.ExType != null)
                return exp.ExType;

            if (!(CheckExpr(exp.Struct) is SymTypeLink) || !(exp.StructMember is SyntaxIdentefier || exp.StructMember is SyntaxDotOp))
                throw new ComException(ERRORS.BadExpression, mTokenizer.Position);

            var var = exp.StructType.Items.FindVar(exp.StructMemberName.Name);
            if (var == null)
                throw new ComException(ERRORS.UnIdefSymbol, mTokenizer.Position);

            if (exp.StructMember is SyntaxDotOp)
            {
                ((SyntaxDotOp)exp.StructMember).Struct.ExType = var.Type;
                exp.ExType = CheckStruct(exp.StructMember as SyntaxDotOp);
            }
            else
                exp.ExType = var.Type;

            return exp.ExType;
        }

        private SymType CheckStruct(SyntaxDotOp exp)
        {
            if (exp.ExType != null)
                return exp.ExType;

            if (!(CheckExpr(exp.Struct) is SymStruct) || !(exp.StructMember is SyntaxIdentefier || exp.StructMember is SyntaxDotOp))
                throw new ComException(ERRORS.BadExpression, mTokenizer.Position);

            var var = exp.StructType.Items.FindVar(exp.StructMemberName.Name);
            if (var == null)
                throw new ComException(ERRORS.UnIdefSymbol, mTokenizer.Position);

            if (exp.StructMember is SyntaxDotOp)
            {
                ((SyntaxDotOp) exp.StructMember).Struct.ExType = var.Type;
                exp.ExType = CheckStruct(exp.StructMember as SyntaxDotOp);
            }
            else
                exp.ExType = var.Type;
            
            return exp.ExType;
        }

        protected SymType CheckIndexArray(SyntaxIndexArray exp)
        {
            var type = CheckExpr(exp.ArrayName);
            if (!( type is SymTypeArray || type is SymTypeLink))
                throw new ComException(ERRORS.BadExpression, mTokenizer.Position);

            for (int i = 0; i < exp.Indexes.Count; i++)
            {
                
                if (!(CheckExpr(exp.Indexes[i]) is SymTypeInt))
                    throw new ComException(ERRORS.BadExpression, mTokenizer.Position);
            }
            
            return exp.ExType = type is SymTypeArray ? ((SymTypeArray) exp.ArrayName.ExType).Type :
                ((SymTypeLink) exp.ArrayName.ExType).Type;
        }

        private static bool CheckScalarTypes(SymType left, SymType right)
        {   return ((left is SymTypeInt || left is SymTypeFloat) && (right is SymTypeFloat || right is SymTypeInt));}

        private static bool CheckLinkType(SymType left, SymType right)
        {   return (left is SymTypeLink && right is SymTypeInt);}

        private SymType CheckBinaryOp(SyntaxBinaryOp exp)
        {

            var left = CheckExpr(exp.Left);
            var right = CheckExpr(exp.Right);

            switch (exp.Operation)
            { 
                case SubTokenType.Sub:
                case SubTokenType.Add:
                case SubTokenType.AddAssignment:
                case SubTokenType.SubAssignment:
                    if (!( CheckScalarTypes(left, right) ||  CheckLinkType(left, right)))
                        throw new ComException(ERRORS.IncorectType, mTokenizer.Position);

                    exp.ExType = (left == right || left is SymTypeFloat || left is SymTypeLink) ? left : right;
                    break;

                case SubTokenType.Equal:
                case SubTokenType.NotEqual:
                case SubTokenType.Low:
                case SubTokenType.Hi:
                case SubTokenType.HiOrEq:
                case SubTokenType.LowOrEq:

                    if (!(CheckScalarTypes(left, right) || left is SymTypeLink && right is SymTypeLink))
                        throw new ComException(ERRORS.IncorectType, mTokenizer.Position);

                    exp.ExType = FindType("int");
                    break;
                
                case SubTokenType.Multiply:
                case SubTokenType.Division:
                case SubTokenType.MultiplyAssigment:
                case SubTokenType.DivisionAssignment:

                    if (!CheckScalarTypes(left, right))
                        throw new ComException(ERRORS.IncorectType, mTokenizer.Position);
                    //предполагаем что ничего кроме инта и флоата быть не может                        
                    exp.ExType = ((left == right) || left is SymTypeFloat) ? left : right;
                    break;

                case SubTokenType.Div:
                case SubTokenType.SHL:
                case SubTokenType.SHR:
                case SubTokenType.DivAssignment:

                case SubTokenType.And:
                case SubTokenType.Or:

                case SubTokenType.LogicalOr:
                case SubTokenType.LogicalAnd:

                case SubTokenType.AndAssignment:
                case SubTokenType.OrAssignment:
                    if (!((left == right) && left is SymTypeInt))
                        throw new ComException(ERRORS.IncorectType, mTokenizer.Position);
                    exp.ExType = left;
                    break;

                case SubTokenType.Assignment:
                    if (left == right)
                        exp.ExType = left;
                    else if (left is SymTypeFloat && right is SymTypeInt || left is SymTypeInt && right is SymTypeFloat)
                        exp.ExType = left;
                    else if (left is SymTypeLink && right is SymTypeArray)
                        exp.ExType = left;
                    else if (right is SymTypeLink && left is SymTypeArray)
                        exp.ExType = right;
                    else
                        throw new ComException(ERRORS.IncorectType, mTokenizer.Position);
                    break;

                default:
                    throw new ComException(ERRORS.UnKnownOp, mTokenizer.Position);
            }
            return exp.ExType;
        }

        private void CheckUnaryOp(SyntaxUnaryOp exp)
        {
            var t = CheckExpr(exp.Argument);
            switch (exp.Operation)
            { 
                case SubTokenType.Increment:
                case SubTokenType.Decrement:
                case SubTokenType.Not:
                    if (!(t is SymTypeInt || t is SymTypeLink))
                        throw new ComException(ERRORS.IncorectType, mTokenizer.Position);
                    break;
                case SubTokenType.Sub:
                case SubTokenType.Add:
                    if (!(t is SymTypeInt || exp.ExType is SymTypeFloat))
                        throw new ComException(ERRORS.IncorectType, mTokenizer.Position);
                    break;
                case SubTokenType.Multiply:
                    if (!(t is SymTypeLink))
                        throw new ComException(ERRORS.IncorectType, mTokenizer.Position);
                    t = (t as SymTypeLink).Type;
                    break;
                case SubTokenType.And:
                    if (t is SyntaxConstInt || t is SyntaxConstReal)
                        throw new ComException(ERRORS.IncorectType, mTokenizer.Position);
                    t = CreateLinkForType(t);
                    break;
            }
            exp.ExType = t;
        }

        protected SymType CheckIdentefier(SyntaxIdentefier exp)
        {
            SymVar var = (SymVar)_useBlock.FindWithParent(exp.Name);
            if (var == null)
                throw new ComException(ERRORS.ExpDef, mTokenizer.Position);
            if (Global.Find(exp.Name) != null)
                exp.IsGlobal = true;
            return var.Type;
        }

        protected SymType CheckConst(SyntaxConst exp)
        {
            if (exp.Type == SubTokenType.CttStr)
                return Global.FindType("charsconsts"); //костыль
            if (exp.Type != SubTokenType.CttInt && exp.Type != SubTokenType.CttFloat)
                throw new ComException(ERRORS.ExpType, mTokenizer.Position);

            return exp.Type == SubTokenType.CttInt ?    Global.FindType("int") : Global.FindType("float");
        }

        protected SymType CheckCallFunc(SyntaxCallFunc exp)
        {
            var funk = (Global.Find(exp.FunkName) as SymFunc);

            //костыль для волшебной функции принтф
            if (exp.FunkName == "printf")
            {
                if (!(exp.Args.First.Value.ExType is SymTypeCharsConst))
                    throw new ComException(ERRORS.TypesNotCoincide, mTokenizer.Position);
                return funk.RetType;
            }

            if (funk.ArgsCount != exp.Args.Count)
                throw new ComException(ERRORS.CountArgs, mTokenizer.Position);

            int i = 0;
            foreach (var n in exp.Args)
            {
                if (!CompateTypes(((SymVar) funk.Args.Vars[i]).Type,n.ExType))
                    throw new ComException(ERRORS.TypesNotCoincide, mTokenizer.Position);
                i++;
            }

            return funk.RetType;
        }

        static bool CompateTypes(SymType a, SymType b)
        {
            return a is SymTypeArray && b is SymTypeLink
                   || b is SymTypeArray && a is SymTypeLink
                   || a == b;
        }

        #endregion
    }
}
